package com.zeta.demo.job.task;

import cn.dev33.satoken.same.SaSameUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import com.fasterxml.jackson.core.type.TypeReference;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.zetaframework.annotation.log.JobLog;
import org.zetaframework.core.utils.ContextUtil;
import org.zetaframework.core.utils.JSONUtil;
import org.zetaframework.core.utils.ZetaFunction;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Xxl-Job 案例
 *
 * @author gcc
 */
@Slf4j
@Component
public class DemoJob {

    /**
     * 域名白名单
     */
    private static final Set<String> DOMAIN_WHITE_LIST = new HashSet<>(Arrays.asList(
            "https://www.baidu.com",
            "https://cn.bing.com",
            "http://localhost:8080"
    ));

    /**
     * 判断url是否在白名单中
     *
     * @param url url
     * @return 是否在白名单中
     */
    private boolean isValidDomain(String url) {
        if (url == null || DOMAIN_WHITE_LIST.isEmpty()) {
            return false;
        }
        for (String prefix : DOMAIN_WHITE_LIST) {
            if (url.startsWith(prefix)) {
                return true;
            }
        }
        return false;
    }


    /**
     * 处理任务，模板方法
     * <p>
     * 说明：<br>
     * 这个方法主要是将处理任务时的通用逻辑抽离成模板
     */
    private void handleJob(String jobName, ZetaFunction handler) throws Exception {
        log.info("====================== {} start ======================", jobName);

        // 获取same-token并设置到ThreadLocal中，防止feign请求报错
        String sameToken = SaSameUtil.getToken();
        ContextUtil.setSameToken(sameToken);

        handler.run();
        log.info("====================== {} end ======================", jobName);
    }


    /**
     * 1、简单任务示例（Bean模式）
     */
    @JobLog(value = "简单任务示例")
    @XxlJob("demoJobHandler")
    public void demoJobHandler() throws Exception {
        handleJob("demoJobHandler", () -> {
            for (int i = 0; i < 5; i++) {
                log.info("demoJobHandler: beat at:" + i);
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    log.error("demoJobHandler: error", e);
                }
            }
        });

        // default success
    }


    /**
     * 2、分片广播任务
     */
    @JobLog(value = "分片广播任务示例")
    @XxlJob("shardingJobHandler")
    public void shardingJobHandler() throws Exception {
        handleJob("shardingJobHandler", () -> {
            // 分片参数
            int shardIndex = XxlJobHelper.getShardIndex();
            int shardTotal = XxlJobHelper.getShardTotal();

            log.info("shardingJobHandler分片参数：当前分片序号 = {}, 总分片数 = {}", shardIndex, shardTotal);

            // 业务逻辑
            for (int i = 0; i < shardTotal; i++) {
                if (i == shardIndex) {
                    log.info("shardingJobHandler: 第 {} 片, 命中分片开始处理", i);
                } else {
                    log.info("shardingJobHandler: 第 {} 片, 忽略", i);
                }
            }
        });
    }

    /**
     * 3、命令行任务
     * <p>
     * 参数示例："ls -a" 或者 "pwd"
     */
    @JobLog(value = "命令行任务示例")
    @XxlJob("commandJobHandler")
    public void commandJobHandler() throws Exception {
        handleJob("commandJobHandler", () -> {
            String command = XxlJobHelper.getJobParam();

            // valid
            if (command == null || command.trim().length() == 0) {
                log.error("command empty.");
                return;
            }

            // command split
            String[] commandArray = command.split(" ");

            // command process
            ProcessBuilder processBuilder = new ProcessBuilder();
            processBuilder.command(commandArray);
            processBuilder.redirectErrorStream(true);

            try {
                Process process = processBuilder.start();

                // 使用 try-with-resources 自动关闭流
                try (BufferedReader reader = new BufferedReader(
                        new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8)
                )) {

                    // 使用 Stream API 逐行读取并记录日志
                    reader.lines().forEach(log::info);
                }

                // command exit
                process.waitFor();
                int exitValue = process.exitValue();

                if (exitValue != 0) {
                    log.error("commandJobHandler：command exit value({}) is failed", exitValue);
                }
            } catch (Exception e) {
                log.error("commandJobHandler：发生异常", e);
                throw new RuntimeException(e);
            }
        });
    }

    /**
     * 4、跨平台Http任务
     * <p>
     * 参数示例：
     * <pre>
     *      {
     *          "url": "https://www.baidu.com",
     *          "method": "get",
     *          "data": "hello world"
     *      }
     *  </pre>
     */
    @JobLog(value = "跨平台http任务示例")
    @XxlJob("httpJobHandler")
    public void httpJobHandler() throws Exception {
        handleJob("httpJobHandler", () -> {
            // 获取任务参数
            String param = XxlJobHelper.getJobParam();
            if (param == null || param.trim().length() == 0) {
                log.error("httpJobHandler: param[{}] invalid.", param);
                return;
            }

            // 参数转换 string -> map
            Map<String, String> paramMap;
            try {
                paramMap = JSONUtil.parseObject(param, new TypeReference<>() {
                });
            } catch (Exception e) {
                log.error("httpJobHandler: Failed to parse param: {}", e.getMessage(), e);
                return;
            }
            if (paramMap == null) {
                log.error("httpJobHandler: paramMap is null.");
                return;
            }

            // 获取参数数据
            String url = paramMap.get("url");
            String method = paramMap.get("method");
            String data = paramMap.get("data");

            // 参数值校验
            if (url == null || url.trim().isEmpty()) {
                log.error("httpJobHandler: url[{}] invalid.", url);
                return;
            }
            if (!isValidDomain(url)) {
                log.error("httpJobHandler: url[{}] not allowed.", url);
                return;
            }
            if (method == null || !Arrays.asList("GET", "POST").contains(method.toUpperCase())) {
                log.error("httpJobHandler: method[{}] invalid.", method);
                return;
            }
            method = method.toUpperCase();
            boolean isPostMethod = "POST".equals(method);

            // 使用Hutool的HttpUtil进行网络请求
            Method httpMethod = isPostMethod? Method.POST: Method.GET;
            try (HttpResponse response = HttpUtil.createRequest(httpMethod, url).body(data).execute()) {
                // 校验状态码
                if (!response.isOk()) {
                    throw new RuntimeException(StrUtil.format("Http Request StatusCode({}) Invalid.", response.getStatus()));
                }
                // 打印响应体
                log.info("httpJobHandler: request success. status: {}, body: {}", response.getStatus(), response.body());
            } catch (Exception e) {
                log.error("httpJobHandler: request failed", e);
            }
        });
    }

    /**
     * 5、生命周期任务示例：任务初始化与销毁时，支持自定义相关逻辑；
     */
    @JobLog(value = "生命周期任务示例")
    @XxlJob(value = "demoJobHandler2", init = "init", destroy = "destroy")
    public void demoJobHandler2() throws Exception {
        handleJob("demoJobHandler2", () -> {
            log.info("task life cycle running....");
        });
    }
    public void init() {
        log.info("task init...");
    }
    public void destroy() {
        log.info("task destroy...");
    }

}
